<template>
  <div class="blur-effect">
    <h1>模糊效果</h1>
    <p>这个示例展示了WebGL中的高斯模糊后期处理效果。通过多次采样周围像素并应用高斯权重，可以实现平滑的模糊效果。</p>

    <el-card class="demo-section">
      <template #header>
        <div class="card-header">
          <h3>模糊效果演示</h3>
        </div>
      </template>
      <div class="demo-content">
        <canvas ref="demoCanvas" width="400" height="300"></canvas>
        <div class="control-panel">
          <el-form :model="blurForm" label-width="120px">
            <el-form-item label="模糊强度">
              <el-slider
                v-model="blurForm.blurIntensity"
                :min="0"
                :max="10"
                :step="0.5"
                @input="updateBlur"
              />
            </el-form-item>
            <el-form-item label="模糊方向">
              <el-select v-model="blurForm.blurDirection" @change="updateBlur">
                <el-option label="水平" value="horizontal" />
                <el-option label="垂直" value="vertical" />
                <el-option label="双向" value="both" />
              </el-select>
            </el-form-item>
            <el-form-item label="启用模糊">
              <el-switch v-model="blurForm.enableBlur" @change="updateBlur" />
            </el-form-item>
          </el-form>
          <p>这是一个高斯模糊后期处理效果，你可以：</p>
          <ul>
            <li>调整模糊强度</li>
            <li>选择模糊方向</li>
            <li>开启/关闭模糊效果</li>
          </ul>
        </div>
      </div>
    </el-card>

    <el-card class="code-section">
      <template #header>
        <div class="card-header">
          <h3>示例代码</h3>
        </div>
      </template>
      <div class="code-content">
        <pre><code>
// 顶点着色器
{{ VSHADER_SOURCE }}

// 片元着色器
{{ FSHADER_SOURCE }}
        </code></pre>
      </div>
    </el-card>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, reactive, onUnmounted } from 'vue'
import { WebGLUtils } from '@/utils/webgl'

const demoCanvas = ref<HTMLCanvasElement | null>(null)
let gl: WebGLRenderingContext | null = null
let program: WebGLProgram | null = null
let webglUtils: WebGLUtils | null = null
let animationId: number | null = null
let texture: WebGLTexture | null = null

const blurForm = reactive({
  blurIntensity: 2.0,
  blurDirection: 'both',
  enableBlur: true
})

// 顶点着色器
const VSHADER_SOURCE = `
  attribute vec4 a_Position;
  attribute vec2 a_TexCoord;
  varying vec2 v_TexCoord;
  void main() {
    gl_Position = a_Position;
    v_TexCoord = a_TexCoord;
  }
`

// 片元着色器
const FSHADER_SOURCE = `
  precision mediump float;
  uniform sampler2D u_Texture;
  uniform vec2 u_TextureSize;
  uniform float u_BlurIntensity;
  uniform int u_BlurDirection; // 0: horizontal, 1: vertical, 2: both
  uniform bool u_EnableBlur;
  varying vec2 v_TexCoord;

  void main() {
    if (!u_EnableBlur) {
      gl_FragColor = texture2D(u_Texture, v_TexCoord);
      return;
    }

    vec4 color = vec4(0.0);
    vec2 texelSize = 1.0 / u_TextureSize;

    // 高斯模糊权重
    float weights[5];
    weights[0] = 0.227027;
    weights[1] = 0.1945946;
    weights[2] = 0.1216216;
    weights[3] = 0.054054;
    weights[4] = 0.016216;

    // 中心像素
    color += texture2D(u_Texture, v_TexCoord) * weights[0];

    for (int i = 1; i < 5; i++) {
      vec2 offset = vec2(0.0);

      if (u_BlurDirection == 0 || u_BlurDirection == 2) {
        // 水平模糊
        offset.x = texelSize.x * float(i) * u_BlurIntensity;
        color += texture2D(u_Texture, v_TexCoord + offset) * weights[i];
        color += texture2D(u_Texture, v_TexCoord - offset) * weights[i];
      }

      if (u_BlurDirection == 1 || u_BlurDirection == 2) {
        // 垂直模糊
        offset = vec2(0.0, texelSize.y * float(i) * u_BlurIntensity);
        color += texture2D(u_Texture, v_TexCoord + offset) * weights[i];
        color += texture2D(u_Texture, v_TexCoord - offset) * weights[i];
      }
    }

    gl_FragColor = color;
  }
`

function initVertexBuffers(gl: WebGLRenderingContext): boolean {
  // 全屏四边形
  const vertices = new Float32Array([
    -1.0, -1.0, 0.0, 0.0,
     1.0, -1.0, 1.0, 0.0,
    -1.0,  1.0, 0.0, 1.0,
     1.0,  1.0, 1.0, 1.0
  ])

  const buffer = gl.createBuffer()
  if (!buffer) {
    console.error('Failed to create buffer')
    return false
  }

  gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
  gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)

  const a_Position = gl.getAttribLocation(program!, 'a_Position')
  const a_TexCoord = gl.getAttribLocation(program!, 'a_TexCoord')

  gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 16, 0)
  gl.enableVertexAttribArray(a_Position)

  gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, 16, 8)
  gl.enableVertexAttribArray(a_TexCoord)

  return true
}

function createTexture(gl: WebGLRenderingContext): WebGLTexture | null {
  const texture = gl.createTexture()
  if (!texture) return null

  // 创建一个简单的渐变纹理作为演示
  const canvas = document.createElement('canvas')
  canvas.width = 256
  canvas.height = 256
  const ctx = canvas.getContext('2d')!

  // 绘制彩色渐变
  const gradient = ctx.createRadialGradient(128, 128, 0, 128, 128, 128)
  gradient.addColorStop(0, '#ff0000')
  gradient.addColorStop(0.3, '#00ff00')
  gradient.addColorStop(0.6, '#0000ff')
  gradient.addColorStop(1, '#ffff00')

  ctx.fillStyle = gradient
  ctx.fillRect(0, 0, 256, 256)

  // 添加一些几何图形
  ctx.fillStyle = '#ffffff'
  ctx.fillRect(50, 50, 50, 50)
  ctx.fillRect(150, 150, 50, 50)

  ctx.fillStyle = '#000000'
  ctx.beginPath()
  ctx.arc(128, 128, 30, 0, Math.PI * 2)
  ctx.fill()

  gl.bindTexture(gl.TEXTURE_2D, texture)
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas)
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)

  return texture
}

function updateBlur() {
  if (!gl || !program) return

  const u_BlurIntensity = gl.getUniformLocation(program, 'u_BlurIntensity')
  const u_BlurDirection = gl.getUniformLocation(program, 'u_BlurDirection')
  const u_EnableBlur = gl.getUniformLocation(program, 'u_EnableBlur')

  gl.uniform1f(u_BlurIntensity, blurForm.blurIntensity)
  gl.uniform1i(u_BlurDirection,
    blurForm.blurDirection === 'horizontal' ? 0 :
    blurForm.blurDirection === 'vertical' ? 1 : 2
  )
  gl.uniform1i(u_EnableBlur, blurForm.enableBlur ? 1 : 0)

  draw()
}

function draw() {
  if (!gl || !program) return

  gl.clear(gl.COLOR_BUFFER_BIT)
  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4)
}

function animate() {
  draw()
  animationId = requestAnimationFrame(animate)
}

function init() {
  if (!demoCanvas.value) return

  gl = demoCanvas.value.getContext('webgl')
  if (!gl) {
    console.error('WebGL not supported')
    return
  }

  webglUtils = new WebGLUtils(gl)
  // 创建着色器
  const vertexShader = webglUtils.createShader(gl.VERTEX_SHADER, VSHADER_SOURCE)
  const fragmentShader = webglUtils.createShader(gl.FRAGMENT_SHADER, FSHADER_SOURCE)
  if (!vertexShader || !fragmentShader) return
  program = webglUtils.createProgram(vertexShader, fragmentShader)
  if (!program) {
    console.error('Failed to create program')
    return
  }

  gl.useProgram(program)

  if (!initVertexBuffers(gl)) {
    console.error('Failed to initialize vertex buffers')
    return
  }

  texture = createTexture(gl)
  if (!texture) {
    console.error('Failed to create texture')
    return
  }

  // 设置uniform变量
  const u_Texture = gl.getUniformLocation(program, 'u_Texture')
  const u_TextureSize = gl.getUniformLocation(program, 'u_TextureSize')

  gl.uniform1i(u_Texture, 0)
  gl.uniform2f(u_TextureSize, demoCanvas.value.width, demoCanvas.value.height)

  gl.activeTexture(gl.TEXTURE0)
  gl.bindTexture(gl.TEXTURE_2D, texture)

  gl.clearColor(0.0, 0.0, 0.0, 1.0)
  gl.viewport(0, 0, demoCanvas.value.width, demoCanvas.value.height)

  updateBlur()
  animate()
}

onMounted(() => {
  init()
})

onUnmounted(() => {
  if (animationId) {
    cancelAnimationFrame(animationId)
  }
})
</script>

<style scoped>
.blur-effect {
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;
}

h1 {
  color: #303133;
  margin-bottom: 20px;
}

p {
  line-height: 1.6;
  color: #606266;
  margin-bottom: 30px;
}

.demo-section {
  margin-bottom: 30px;
}

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.demo-content {
  display: flex;
  gap: 30px;
  align-items: flex-start;
}

canvas {
  border: 1px solid #dcdfe6;
  border-radius: 4px;
}

.control-panel {
  flex: 1;
  min-width: 300px;
}

.control-panel p {
  margin: 20px 0 10px;
  font-weight: bold;
  color: #409EFF;
}

.control-panel ul {
  list-style-type: disc;
  padding-left: 20px;
  color: #606266;
}

.control-panel li {
  margin: 5px 0;
}

.code-section {
  margin-top: 30px;
}

.code-content {
  background-color: #f5f7fa;
  border-radius: 4px;
  overflow-x: auto;
}

.code-content pre {
  margin: 0;
  padding: 20px;
  font-family: 'Courier New', monospace;
  font-size: 14px;
  line-height: 1.5;
  color: #303133;
}
</style>
